package com.bianmaba.services.datacollection;

import com.bianmaba.beans.datacollection.CollectionSchedule;
import com.bianmaba.beans.datacollection.CollectionTask;
import com.bianmaba.beans.datacollection.ExecuteLog;
import com.bianmaba.beans.datacollection.TaskInstance;
import com.bianmaba.commons.bean.result.OperationResult;
import com.bianmaba.dc.bean.CollectorResponse;
import com.bianmaba.dc.bean.JobParameter;
import com.bianmaba.dc.bean.TaskStatus;
import com.bianmaba.dc.bean.TaskType;
import com.bianmaba.remote.supports.CollectorRemoteRequestService;
import com.bianmaba.repositories.datacollection.CollectionScheduleRepository;
import com.bianmaba.services.datacollection.quartiz.QuartzTaskManager;
import com.bianmaba.spring.web.service.BaseService;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.List;
import java.util.Map;

@Service
@Transactional
public class CollectionScheduleService extends BaseService<CollectionSchedule, String> implements ICollectionScheduleService {
    @Autowired
    private CollectionScheduleRepository repository;
    @Autowired
    private QuartzTaskManager quartzTaskManager;
    @Autowired
    private ICollectionTaskService collectionTaskService;
    @Autowired
    private ITaskInstanceService taskInstanceService;
    @Autowired
    private CollectorRemoteRequestService remoteRequestService;
    @Autowired
    protected ExecuteLogRecorder logWriter;

    @Override
    public CollectionScheduleRepository getRepository() {
        return repository;
    }


    @Override
    public CollectionSchedule saveAndStartSchedule(CollectionSchedule entity) throws ClassNotFoundException {
        entity = super.save(entity);
        if (!entity.getDisabled()) {
            startSchedule(entity.getId(), "计划执行");
        } else {
            stopBySchedulId(entity.getId());
        }
        return entity;
    }

    /**
     * 终止指定采集计划及任务
     *
     * @param collectionScheduleId
     * @return
     */
    @Override
    public boolean stopBySchedulId(String collectionScheduleId) {
        try {
            //更新采集计划是否关闭的状态
            CollectionSchedule schedule = getById(collectionScheduleId);
            schedule.setDisabled(true);
            save(schedule);

            //准备停止现有JOB
            String jobName = schedule.getTask().getId();
            String groupName = schedule.getProjectId() + "->计划执行";
            stopByJobKey(JobKey.jobKey(jobName, groupName));
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public boolean stopByJobKey(JobKey jobKey) {
        try {
            quartzTaskManager.deleteJob(jobKey);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public Trigger executeOnce(String taskId, String executor) throws ClassNotFoundException {
        return executeOnce(taskId, executor, null);
    }

    @Override
    public Trigger executeOnce(String taskId, String executor, Map<String, String> overwriteValues) throws ClassNotFoundException {
        CollectionTask task = collectionTaskService.findOne(taskId);
        Class clazz = Class.forName(task.getCollectionInterface().getJobClass());
        //创建jobData,包含任务实例信息，项目信息，接口信息及采集参数
        JobDataMap jobDatas = createJobDatas(null, task, executor, overwriteValues);
        String jobName = task.getId();
        String jobGroup = task.getProjectId() + "->手动执行";
        Trigger trigger = quartzTaskManager.executeOnceJob(clazz, jobName, jobGroup, jobDatas);
        return trigger;
    }

    /**
     * 终止指定的任务实例
     *
     * @param taskInstanceId
     * @return
     */
    @Override
    public OperationResult stopJob(String taskInstanceId) {
        TaskInstance taskInstance = taskInstanceService.getById(taskInstanceId);
        String url = taskInstance.getCollectionTask().getCollectionInterface().getBaseUrl() + "stop";

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        map.add("taskInstanceId", taskInstanceId);
        HttpEntity<Map> entity = new HttpEntity(map, headers);

        CollectorResponse result = (CollectorResponse) remoteRequestService.post(url, entity);
        if (!result.isSuccess()) {
            return OperationResult.of(true).setResult("任务终止请求发送失败，请检查采集器是否运行！");
        } else {
            if (result.getTaskStatus() != null && TaskStatus.SYSTEM_TERMINATED.equals(result.getTaskStatus())) {
                taskInstanceService.updateStatus(taskInstanceId, result.getTaskStatus().getIndex());
                if (taskInstance.getCollectionSchedule() != null) {
                    stopBySchedulId(taskInstance.getCollectionSchedule().getId());
                } else {
                    String jobName = taskInstance.getCollectionTask().getId();
                    String groupName = taskInstance.getCollectionTask().getProjectId() + "->手动执行";
                    //停止任务请求JOB
                    stopByJobKey(JobKey.jobKey(jobName, groupName));
                    //停止任务状态监听JOB
                    stopByJobKey(JobKey.jobKey(taskInstance.getId(), "采集任务状态监听"));
                }
                return OperationResult.of(true).setResult("采集器中不存在此任务，服务器任务终止");
            } else {
                if (result.getTaskStatus() != null && !TaskStatus.RUNNING.equals(result.getTaskStatus())) {
                    if (!result.getMessages().isEmpty()) {
                        List<String> messages = result.getMessages();
                        for (String message : messages) {
                            logWriter.put(ExecuteLog.of(taskInstanceId, message));
                        }
                    }
                    taskInstanceService.updateStatus(taskInstanceId, result.getTaskStatus().getIndex());
                    return OperationResult.of(true).setResult("任务已经执行结束或终止！");
                } else {
                    return OperationResult.of(true).setResult("任务终止请求已经发送到采集器！");
                }
            }
        }
    }

    /**
     * 启动一个采集计划
     *
     * @param collectionScheduleId
     * @param executor
     * @return
     * @throws ClassNotFoundException
     */
    private Trigger startSchedule(String collectionScheduleId, String executor) throws ClassNotFoundException {
        CollectionSchedule schedule = getById(collectionScheduleId);
        CollectionTask task = schedule.getTask();
        JobDataMap jobDataMap = createJobDatas(schedule, task, executor);
        String jobName = task.getId();
        String jobGroup = task.getProjectId() + "->计划执行";
        Class clazz = Class.forName(task.getCollectionInterface().getJobClass());
        return quartzTaskManager.scheduleJob(clazz,
                jobName,
                jobGroup,
                schedule.getCron(),
                jobDataMap,
                schedule.getStartDatetime());
    }

    private JobDataMap createJobDatas(CollectionSchedule schedule, CollectionTask task, String executor) {
        return createJobDatas(schedule, task, executor, null);
    }

    /**
     * 创建JobData
     *
     * @param schedule
     * @param task
     * @param executor
     * @return
     */
    private JobDataMap createJobDatas(CollectionSchedule schedule, CollectionTask task, String executor, Map<String, String> overwriteValues) {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put(JobParameter.TASK_TYPE.getName(), TaskType.DATA_COLLECTION_TASK);
        jobDataMap.put(JobParameter.PROJECT_ID.getName(), task.getProjectId());
        jobDataMap.put(JobParameter.TASK_ID.getName(), task.getId());
        jobDataMap.put(JobParameter.EXECUTOR.getName(), executor);

        if (schedule != null) {
            jobDataMap.put(JobParameter.SCHEDULE_ID.getName(), schedule.getId());
            jobDataMap.put(JobParameter.SCHEDULE_NAME.getName(), schedule.getName());
        }
        jobDataMap.put("overwrite_values", overwriteValues);
        jobDataMap.put(JobParameter.COLLECTION_PARAMETERS.getName(), task.getParameterMaps());
        return jobDataMap;
    }

}
